package schedule;

import java.util.ArrayList;

import plan.Plan;
import start.Start;
import auftrag.Auftrag;
import auftrag.Produkt;
import auftrag.Ressource;

public class IterateProductOrderThreshold implements ISchedule {
	long lMaxTime = 60000;
	double t = 500;
	
	@Override
	public Plan makePlan(ArrayList<?> pAuftragList,
			ArrayList<Ressource> pRessourceList) {
		Plan p = new Plan();
		int it = 0;
		//sortiere Aufträge nach EDF, dann nach Priorität
		ArrayList<Produkt> prodList = new ArrayList<Produkt>();
		if(Auftrag.class.isInstance(pAuftragList.get(0))){
			Auftrag aufAuftrag;
			ArrayList<Auftrag> arrSortAuftrag = new ArrayList<Auftrag>();
			for (int intIndexa = 0; intIndexa < pAuftragList.size(); intIndexa++) {
				aufAuftrag = (Auftrag) pAuftragList.get(intIndexa);
				if (arrSortAuftrag.isEmpty()) {
					arrSortAuftrag.add(aufAuftrag);
				} else {
					for (int intIndexb = 0; intIndexb < arrSortAuftrag.size(); intIndexb++) {
						if (aufAuftrag.getDeadline() < arrSortAuftrag
								.get(intIndexb).getDeadline()) {
							arrSortAuftrag.add(intIndexb, aufAuftrag);
							intIndexb = arrSortAuftrag.size();
						}
						else if (aufAuftrag.getDeadline() == arrSortAuftrag.get(
								intIndexb).getDeadline()
								&& aufAuftrag.getPriority() > arrSortAuftrag.get(
										intIndexb).getPriority()) {
							arrSortAuftrag.add(intIndexb, aufAuftrag);
							intIndexb = arrSortAuftrag.size();
						}
						else if((intIndexb+1) >= arrSortAuftrag.size() ){
							arrSortAuftrag.add(aufAuftrag);
							intIndexb = arrSortAuftrag.size();
						}
					}
				}
			}
			for(Auftrag a : arrSortAuftrag){
				prodList.addAll(a.getAuftragProdukte());
			}
		}
		//Initial-Plan nach EDF
		p = new SimplePlan().makePlan(prodList, pRessourceList);
		System.out.println("Intial plan's Quality: "+p.getQuality(true)+"\tt: "+t);
		
		int quality = 0;
		int nProd1, nProd2, nProd3, nProd4, nCountNothingChanged = 0;
		long lStart = System.currentTimeMillis();
		while(System.currentTimeMillis()-lStart < lMaxTime-10000 && p.getQuality(false) > 0 && nCountNothingChanged < 100){
			Start.it++;
			Plan pNeighbour;
			quality = p.getQuality(false);
			//wähle vier Produkte aus, wobei 3 und 4 gleich sein dürfen, damit auch nur ein Produkt vertauscht werden kann
			do {
				nProd1 = (int) (Math.random()*prodList.size());
				nProd2 = (int) (Math.random()*prodList.size());
				nProd3 = (int) (Math.random()*prodList.size());
				nProd4 = (int) (Math.random()*prodList.size());
			} while(nProd1 == nProd2 || nProd1 == nProd3 || nProd1 == nProd4 || nProd2 == nProd3 || nProd2 == nProd4);
			
			//tausche Produkte
			prodList = switchProducts(prodList, nProd1, nProd2);
			if(nProd3 != nProd4)
				prodList = switchProducts(prodList, nProd3, nProd4);

			for(Ressource r : pRessourceList)
				r.clear();
			
			//baue neuen Plan
			pNeighbour = new SimplePlan().makePlan(prodList, pRessourceList);
			
			//falls Plan besser, behalten
			if(pNeighbour.getQuality(true) < quality + t){
				p = pNeighbour;
				nCountNothingChanged = 0;			
			//sonst: Rollback, t verkleinern
			} else {
				if(nProd3 != nProd4)
					prodList = switchProducts(prodList, nProd3, nProd4);
				prodList = switchProducts(prodList, nProd1, nProd2);
				
				nCountNothingChanged++;
				if(nCountNothingChanged > 0 && nCountNothingChanged % 3 == 0){
					t=t*0.85;
					
					IterateVariants itv = new IterateVariants();
					itv.setMaxTime(10000);
					itv.setTemperature(t);
					p = itv.makePlan(prodList, pRessourceList);

				} else { 
					//p = new SimplePlan().makePlan(prodList, pRessourceList);
				}
				
				if(t < 0.001)
					t = 0;
				
			}
			System.out.println((System.currentTimeMillis()-lStart)+" IP: Quality:"+p.getQuality(false)+"\tt:"+t);
		}
		System.err.println("Iterations: "+it);
		return p;
	}
	
	private ArrayList<Produkt> switchProducts(ArrayList<Produkt> list, int pos1, int pos2){
		Produkt tmp = list.get(pos1);
		list.set(pos1, list.get(pos2));
		list.set(pos2, tmp);
		
		return list;
	}
	
	public void setMaxTime(long t){
		lMaxTime = t;
	}
}
